{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Sympy variables are created using unique string identifiers."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import sympy as sp\n",
        "import numpy as np\n",
        "x = sp.Symbol(\"x\")\n",
        "y = sp.Symbol(\"y\")\n",
        "z = sp.Symbol(\"z\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "One can form expression from symbols.\n",
        "Sympy expressions are made up of numbers, symbols, and sympy functions."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "x**2.0 + y**2.0 + z**2.0"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "expression = x**2. + y**2. + z ** 2.\n",
        "expression"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Two expressions may be added together to form a new one."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "2*x**2.0 + y**2.0 + z**2.0"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "other_expression = x**2.\n",
        "expression += other_expression\n",
        "expression"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "One can form sympy `Matrix` objects."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[1, 2],\n",
              "[3, 4]])"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "sp.Matrix([[1,2],[3,4]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "An important `Matrix` function is `eye(n)`, which forms a $n \\times n$ identity matrix."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[1, 0, 0],\n",
              "[0, 1, 0],\n",
              "[0, 0, 1]])"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "sp.eye(3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "One can stuff expressions into matrices, too."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[x**2.0 + y**2 - z**2.0],\n",
              "[           2*x + y + z]])"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# One can stuff expressions into matrices\n",
        "f1 = x**2.+y**2-z**2.\n",
        "f2 = 2*x + y + z\n",
        "function_matrix = sp.Matrix([f1,f2])\n",
        "function_matrix"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "One may compute the Jacobian of vector valued functions, too."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[2.0*x**1.0, 2*y, -2.0*z**1.0],\n",
              "[         2,   1,           1]])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function_matrix.jacobian([x,y,z]) # pass in a list of Sympy Symbols to take the Jacobian"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Sympy expressions can be evaluated by passing in a Python dictionary mapping Symbol `Symbol`s to specific values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "-4.00000000000000"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "x_val = 1.0\n",
        "y_val = 2.0\n",
        "z_val = 3.0\n",
        "values={\"x\":x_val,\"y\":y_val,\"z\":z_val}\n",
        "f1.subs(values)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "One can even valuate the Jacobian of functions."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[2.0, 4.0, -6.0],\n",
              "[  2,   1,    1]])"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "J_mat = function_matrix.jacobian([x,y,z]).subs(values)\n",
        "J_mat"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "To convert a Sympy `Matrix` into a Numpy array, one may use the following:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[2.00000000000000, 4.00000000000000, -6.00000000000000],\n",
              "       [2, 1, 1]], dtype=object)"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.array(J_mat)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "After evaluating an expression in Sympy, the return type is a `sympy.Float`.\n",
        "However, this is not readily usable by Numpy. Therefore, consider casting `sympy.Float` to a `numpy.float64`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 2.,  4., -6.],\n",
              "       [ 2.,  1.,  1.]])"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "J=np.array(J_mat).astype(np.float64)\n",
        "J"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "At this point, one cna do all the usual stuff one would in Numpy."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[  8.,  10., -10.],\n",
              "       [ 10.,  17., -23.],\n",
              "       [-10., -23.,  37.]])"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "J.T@J"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Symp's `Lambdify` can help increase the speed of Sympy's numerical computations."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Matrix([\n",
              "[-4.0],\n",
              "[ 7.0]])"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function_matrix.subs(values)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-4.],\n",
              "       [ 7.]])"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from sympy.utilities.lambdify import lambdify\n",
        "array2mat = [{'ImmutableDenseMatrix': np.array}, 'numpy']\n",
        "lam_f_mat = lambdify((x,y,z), function_matrix, modules=array2mat)\n",
        "lam_f_mat(1,2,3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Or if it is more convenient to have the function evaluation occur from a list of some sort, the Python `*` operator on lists can help."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-4.],\n",
              "       [ 7.]])"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "lam_f_mat(*[1,2,3])"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.7rc1"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}